﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Data.SqlClient;
using System.Data;
using MockCommon;
using MockDataTransferObject;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Data.Common;

namespace MockDataAccess
{
    public class BaseDataAccess<T>
    {
        #region Initialize
        private bool IsFake;
        public Collection<T> AffectedRows { get; private set; }
        public SqlConnection ConnectionObject { get; private set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="BaseDataAccess&lt;T&gt;"/> class.
        /// </summary>
        protected BaseDataAccess()
        {
            IsFake = false;
            ConnectionObject = Connection.RetrieveConnection();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="BaseDataAccess&lt;T&gt;"/> class.
        /// </summary>
        /// <param name="isFake">if set to <c>true</c> [is fake].</param>
        protected BaseDataAccess(bool isFake)
        {
            this.IsFake = isFake;
            ConnectionObject = Connection.RetrieveConnection();
            if (isFake)
            {
                AffectedRows = new Collection<T>();
            }
        }

        /// <summary>
        /// Opens the connection.
        /// </summary>
        /// <param name="connection">The connection.</param>
        public void OpenConnection(DbConnection connection)
        {
            try
            {
                if (connection.State == ConnectionState.Closed)
                {
                    connection.Open();
                }
            }
            catch (InvalidOperationException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (SqlException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (ArgumentException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
        }

        /// <summary>
        /// Closes the connection.
        /// </summary>
        /// <param name="connection">The connection.</param>
        public void CloseConnection(DbConnection connection)
        {
            try
            {
                if (connection.State == ConnectionState.Open)
                {
                    connection.Close();
                }
            }
            catch (SqlException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
        }

        /// <summary>
        /// Executes the query as scalar.
        /// </summary>
        /// <param name="query">The query.</param>
        /// <param name="parameters">The parameters.</param>
        /// <returns></returns>
        public object ExecuteQueryAsScalar(string query, Collection<SqlParameter> parameters)
        {
            try
            {
                OpenConnection(ConnectionObject);
                var command = new SqlCommand(query, ConnectionObject);
                if (parameters != null)
                {
                    foreach (SqlParameter parameter in parameters)
                    {
                        command.Parameters.Add(parameter);
                    }
                }

                return command.ExecuteScalar();
            }
            catch (InvalidOperationException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (ArgumentException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (InvalidCastException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (SqlException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            finally
            {
                CloseConnection(ConnectionObject);
            }
        }

        /// <summary>
        /// Executes the query as scalar.
        /// </summary>
        /// <param name="query">The query.</param>
        /// <param name="parameters">The parameters.</param>
        /// <param name="transaction">The transaction.</param>
        /// <returns></returns>
        public object ExecuteQueryAsScalar(string query, Collection<SqlParameter> parameters, SqlTransaction transaction)
        {
            try
            {
                var command = new SqlCommand(query, ConnectionObject, transaction);
                if (parameters != null)
                {
                    foreach (SqlParameter parameter in parameters)
                    {
                        command.Parameters.Add(parameter);
                    }
                }

                return command.ExecuteScalar();
            }
            catch (ArgumentException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (InvalidCastException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (SqlException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
        }

        /// <summary>
        /// Executes the query as data table.
        /// </summary>
        /// <param name="query">The query.</param>
        /// <param name="parameters">The parameters.</param>
        /// <returns></returns>
        public DataTable ExecuteQueryAsDataTable(string query, Collection<SqlParameter> parameters)
        {
            try
            {
                OpenConnection(ConnectionObject);
                var command = new SqlCommand(query, ConnectionObject);
                if (parameters != null)
                {
                    foreach (SqlParameter parameter in parameters)
                    {
                        command.Parameters.Add(parameter);
                    }
                }

                var adapter = new SqlDataAdapter(command);
                var dataTable = new DataTable("DataTable");
                dataTable.Locale = CultureInfo.CurrentCulture;
                adapter.Fill(dataTable);
                return dataTable;
            }
            catch (InvalidOperationException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (ArgumentException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (InvalidCastException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (SqlException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            finally
            {
                CloseConnection(ConnectionObject);
            }
        }

        /// <summary>
        /// Executes the non query.
        /// </summary>
        /// <param name="commandText">The command text.</param>
        /// <param name="parameters">The parameters.</param>
        /// <returns></returns>
        public bool ExecuteNonQuery(string commandText, Collection<SqlParameter> parameters)
        {
            try
            {
                OpenConnection(ConnectionObject);
                SqlCommand command = new SqlCommand(commandText, ConnectionObject);
                if (parameters != null)
                {
                    foreach (SqlParameter parameter in parameters)
                    {
                        command.Parameters.Add(parameter);
                    }
                }

                int affectedRow = command.ExecuteNonQuery();
                if (affectedRow > 0)
                {
                    return true;
                }
                return false;
            }
            catch (ArgumentException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (InvalidCastException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (SqlException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            finally
            {
                CloseConnection(ConnectionObject);
            }
        }

        /// <summary>
        /// Executes the non query.
        /// </summary>
        /// <param name="commandText">The command text.</param>
        /// <param name="parameters">The parameters.</param>
        /// <param name="transaction">The transaction.</param>
        /// <returns></returns>
        public bool ExecuteNonQuery(string commandText, Collection<SqlParameter> parameters, SqlTransaction transaction)
        {
            try
            {
                var command = new SqlCommand(commandText, ConnectionObject, transaction);
                if (parameters != null)
                {
                    foreach (SqlParameter parameter in parameters)
                    {
                        command.Parameters.Add(parameter);
                    }
                }

                int affectedRow = command.ExecuteNonQuery();
                if (affectedRow > 0)
                {
                    return true;
                }
                return false;
            }
            catch (ArgumentException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (InvalidCastException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (SqlException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
        }

        #endregion
        #region CommonDataAccess
        /// <summary>
        /// Exists the specified id.
        /// </summary>
        /// <param name="id">The id.</param>
        /// <param name="transaction">The transaction.</param>
        /// <param name="type">The type.</param>
        /// <returns></returns>
        private bool Exist(object id, SqlTransaction transaction, Type type)
        {
            try
            {
                AbstractEntity data = (AbstractEntity)Activator.CreateInstance(type);

                string tableName = String.Format(CultureInfo.CurrentCulture, "[{0}]", type.Name);
                string primaryKey = data.RetrievePrimaryKeyColumn();
                string wherePart = String.Format(CultureInfo.CurrentCulture, "[{0}]='{1}'", primaryKey, id);
                string query = String.Format(CultureInfo.CurrentCulture, Constant.QuerySelect, String.Format(CultureInfo.CurrentCulture, "top(1) [{0}]", primaryKey), tableName, wherePart);
                object value;
                if (transaction != null)
                {
                    value = ExecuteQueryAsScalar(query, null, transaction);
                }
                else
                {
                    value = ExecuteQueryAsScalar(query, null);
                }
                return (value != null);
            }
            catch (ArgumentException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (FormatException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (NotSupportedException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (TargetInvocationException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (MethodAccessException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (MemberAccessException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (InvalidComObjectException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (COMException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (TypeLoadException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
        }

        /// <summary>
        /// Creates the specified data.
        /// </summary>
        /// <param name="data">The data.</param>
        /// <param name="transaction">The transaction.</param>
        private void Create(AbstractEntity data, SqlTransaction transaction)
        {
            try
            {
                Dictionary<string, object> rows = ConvertToRow(data);
                string tableName = String.Format(CultureInfo.CurrentCulture, "[{0}]", data.GetType().Name);

                string insertColumn = "";
                string insertValue = "";


                if (rows != null)
                {
                    int columnCount = rows.Count;
                    string columnName;
                    object columValue;
                    for (int i = 0; i < columnCount; i++)
                    {
                        columnName = rows.ElementAt(i).Key;
                        columValue = rows.ElementAt(i).Value;
                        if (columValue == null)
                        {
                            insertColumn += String.Format(CultureInfo.CurrentCulture, "[{0}]", columnName);
                            insertValue += "null";
                        }
                        else
                        {
                            if (columValue.GetType().BaseType == typeof(AbstractEntity))
                            {
                                AbstractEntity entity = (AbstractEntity)columValue;
                                Dictionary<string, object> rowsInside = ConvertToRow(entity);
                                columValue = rowsInside[entity.RetrievePrimaryKeyColumn()];

                                if (transaction != null)
                                {
                                    if (!Exist(columValue, transaction, entity.GetType()))
                                    {
                                        Create(entity, transaction);
                                    }
                                }

                            }
                            insertColumn += String.Format(CultureInfo.CurrentCulture, "[{0}]", columnName);
                            insertValue += String.Format(CultureInfo.CurrentCulture, "N'{0}'", columValue);
                        }
                        if (i < columnCount - 1)
                        {
                            insertColumn += ",";
                            insertValue += ",";
                        }
                    }
                }
                string commandText = String.Format(CultureInfo.CurrentCulture, Constant.QueryInsert, tableName, insertColumn, insertValue);
                if (transaction != null)
                {
                    ExecuteNonQuery(commandText, null, transaction);
                }
                else
                {
                    ExecuteNonQuery(commandText, null);
                }
            }
            catch (ArgumentNullException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (FormatException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }

        }

        /// <summary>
        /// Updates the specified data.
        /// </summary>
        /// <param name="data">The data.</param>
        /// <param name="transaction">The transaction.</param>
        private void Update(AbstractEntity data, SqlTransaction transaction)
        {
            try
            {
                Dictionary<string, object> rows = ConvertToRow(data);
                string tableName = String.Format(CultureInfo.CurrentCulture, "[{0}]", data.GetType().Name);
                string primaryKey = data.RetrievePrimaryKeyColumn();

                string updatePart = "";
                string wherePart = "";

                if (rows != null)
                {
                    int columnCount = rows.Count;
                    string columnName;
                    object columValue;
                    for (int i = 0; i < columnCount; i++)
                    {
                        columnName = rows.ElementAt(i).Key;
                        columValue = rows.ElementAt(i).Value;
                        if (columnName.Equals(primaryKey))
                        {
                            wherePart += String.Format(CultureInfo.CurrentCulture, "[{0}]=N'{1}'", columnName, columValue);
                            continue;
                        }

                        if (columValue == null)
                        {
                            updatePart += String.Format(CultureInfo.CurrentCulture, "[{0}]='{1}'", columnName, "null");
                        }
                        else
                        {
                            if (columValue.GetType().BaseType == typeof(AbstractEntity))
                            {
                                AbstractEntity entity = (AbstractEntity)columValue;
                                Dictionary<string, object> rowsInside = ConvertToRow(entity);
                                columValue = rowsInside[entity.RetrievePrimaryKeyColumn()];

                                if (transaction != null)
                                {
                                    Update(entity, transaction);
                                }
                            }
                            updatePart += String.Format(CultureInfo.CurrentCulture, "[{0}]=N'{1}'", columnName, columValue);
                        }

                        if (i < columnCount - 1)
                        {
                            updatePart += ",";
                        }
                    }

                    string commandText = String.Format(CultureInfo.CurrentCulture, Constant.QueryUpdate, tableName, updatePart, wherePart);
                    if (transaction != null)
                    {
                        ExecuteNonQuery(commandText, null, transaction);
                    }
                    else
                    {
                        ExecuteNonQuery(commandText, null);
                    }
                }
            }
            catch (ArgumentNullException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (FormatException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
        }

        /// <summary>
        /// Deletes the specified id.
        /// </summary>
        /// <param name="id">The id.</param>
        /// <param name="type">The type.</param>
        /// <param name="transaction">The transaction.</param>
        private void Delete(object id, Type type, SqlTransaction transaction)
        {
            try
            {
                AbstractEntity data = (AbstractEntity)Activator.CreateInstance(type);
                string tableName = String.Format(CultureInfo.CurrentCulture, "[{0}]", type.Name);
                string primaryKey = data.RetrievePrimaryKeyColumn();
                string wherePart = String.Format(CultureInfo.CurrentCulture, "[{0}]=N'{1}'", primaryKey, id);
                string commandText = String.Format(CultureInfo.CurrentCulture, Constant.QueryDelete, tableName, wherePart);

                if (transaction != null)
                {
                    ExecuteNonQuery(commandText, null, transaction);
                }
                else
                {
                    ExecuteNonQuery(commandText, null);
                }
            }
            catch (ArgumentOutOfRangeException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (ArgumentException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (FormatException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (NotSupportedException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (TargetInvocationException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (MethodAccessException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (MemberAccessException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (InvalidComObjectException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (COMException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (TypeLoadException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
        }

        /// <summary>
        /// Gets the by id.
        /// </summary>
        /// <param name="id">The id.</param>
        /// <param name="type">The type.</param>
        /// <returns></returns>
        private DataTable GetById(object id, Type type)
        {
            try
            {
                AbstractEntity data = (AbstractEntity)Activator.CreateInstance(type);

                string tableName = String.Format(CultureInfo.CurrentCulture, "[{0}]", type.Name);
                string primaryKey = data.RetrievePrimaryKeyColumn();
                string wherePart = String.Format(CultureInfo.CurrentCulture, "[{0}]=N'{1}'", primaryKey, id);
                string query = String.Format(CultureInfo.CurrentCulture, Constant.QuerySelect, "*", tableName, wherePart);

                DataTable table = ExecuteQueryAsDataTable(query, null);
                return table;
            }
            catch (ArgumentException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (FormatException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (NotSupportedException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (TargetInvocationException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (MethodAccessException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (MemberAccessException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (InvalidComObjectException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (COMException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (TypeLoadException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
        }

        /// <summary>
        /// Converts to list.
        /// </summary>
        /// <param name="table">The table.</param>
        /// <param name="type">The type.</param>
        /// <returns></returns>
        protected Collection<AbstractEntity> ConvertToList(DataTable table, Type type)
        {
            try
            {
                Collection<AbstractEntity> listType = new Collection<AbstractEntity>();
                PropertyInfo[] properties = type.GetProperties();
                object instance;
                if (table != null)
                {
                    foreach (DataRow row in table.Rows)
                    {
                        instance = Activator.CreateInstance(type);
                        foreach (PropertyInfo property in properties)
                        {
                            if (property.PropertyType.BaseType == typeof(AbstractEntity))
                            {
                                DataTable tableValue = GetById(row[property.Name], property.PropertyType);
                                Collection<AbstractEntity> entity = ConvertToList(tableValue, property.PropertyType);
                                property.SetValue(instance, entity[0], null);
                            }
                            else
                            {
                                object value = row[property.Name];
                                value = value is DBNull ? null : value;
                                property.SetValue(instance, value, null);
                            }
                        }
                        listType.Add((AbstractEntity)instance);
                    }
                }
                return listType;
            }
            catch (ArgumentException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (FormatException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (NotSupportedException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (TargetInvocationException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (MethodAccessException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (MemberAccessException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (InvalidComObjectException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (COMException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (TypeLoadException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (TargetException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (TargetParameterCountException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
        }

        /// <summary>
        /// Converts to row.
        /// </summary>
        /// <param name="data">The data.</param>
        /// <returns></returns>
        private static Dictionary<string, object> ConvertToRow(AbstractEntity data)
        {
            try
            {
                Type type = data.GetType();
                PropertyInfo[] properties = type.GetProperties();
                Dictionary<string, object> rows = new Dictionary<string, object>();
                foreach (PropertyInfo property in properties)
                {
                    string columnName = property.Name;
                    object columnValue = type.GetProperty(columnName).GetValue(data, null);
                    rows.Add(columnName, columnValue);
                }
                return rows;
            }
            catch (ArgumentException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (TargetException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (TargetParameterCountException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (MethodAccessException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (TargetInvocationException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
        }
        #endregion
        #region <T>DataAccess
        /// <summary>
        /// Exists the specified id.
        /// </summary>
        /// <param name="id">The id.</param>
        /// <returns></returns>
        public bool Exist(object id)
        {
            Type type = typeof(T);
            return Exist(id, null, type);
        }

        /// <summary>
        /// Gets the by id.
        /// </summary>
        /// <param name="id">The id.</param>
        /// <returns></returns>
        public T GetById(object id)
        {
            Type type = typeof(T);
            try
            {
                AbstractEntity data = (AbstractEntity)Activator.CreateInstance(type);

                string tableName = String.Format(CultureInfo.CurrentCulture, "[{0}]", type.Name);
                string primaryKey = data.RetrievePrimaryKeyColumn();
                string wherePart = String.Format(CultureInfo.CurrentCulture, "[{0}]='{1}'", primaryKey, id);
                string query = String.Format(CultureInfo.CurrentCulture, Constant.QuerySelect, "*", tableName, wherePart);

                DataTable table = ExecuteQueryAsDataTable(query, null);
                Collection<AbstractEntity> entities = ConvertToList(table, typeof(T));
                return (T)(entities[0] as object);
            }
            catch (ArgumentOutOfRangeException)
            {
                throw new MockException(String.Format(CultureInfo.CurrentCulture, "{0} not found", type.Name), "DataAccess");
            }
            catch (ArgumentException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (FormatException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (NotSupportedException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (TargetInvocationException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (MethodAccessException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (MemberAccessException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (InvalidComObjectException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (COMException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }
            catch (TypeLoadException exception)
            {
                throw new MockException(exception.Message, "DataAccess");
            }

        }

        /// <summary>
        /// Creates the specified data.
        /// </summary>
        /// <param name="data">The data.</param>
        public void Create(T data)
        {
            try
            {
                AffectedRows = new Collection<T>();
                OpenConnection(ConnectionObject);
                SqlTransaction transaction = ConnectionObject.BeginTransaction();
                Create(data as AbstractEntity, transaction);
                if (IsFake)
                {
                    AffectedRows.Add(data);
                    transaction.Rollback();
                }
                else
                {
                    transaction.Commit();
                }
            }
            finally
            {
                CloseConnection(ConnectionObject);
            }
        }

        /// <summary>
        /// Creates the specified data.
        /// </summary>
        /// <param name="data">The data.</param>
        /// <param name="isCascading">if set to <c>true</c> [is cascading].</param>
        public void Create(T data, bool isCascading)
        {
            if (isCascading)
            {
                AffectedRows = new Collection<T>();
                OpenConnection(ConnectionObject);
                SqlTransaction transaction = null;
                try
                {

                    transaction = ConnectionObject.BeginTransaction();
                    Create(data as AbstractEntity, transaction);
                    if (IsFake)
                    {
                        AffectedRows.Add(data);
                        transaction.Rollback();
                    }
                    else
                    {
                        transaction.Commit();
                    }
                }
                catch (InvalidOperationException exception)
                {
                    throw new MockException(exception.Message, "DataAccess");
                }
                catch (SqlException exception)
                {
                    throw new MockException(exception.Message, "DataAccess");
                }
                catch (MockException)
                {
                    if (transaction != null)
                        transaction.Rollback();
                    throw;
                }
                finally
                {
                    CloseConnection(ConnectionObject);
                }
            }
            else
            {
                Create(data);
            }
        }

        /// <summary>
        /// Updates the specified data.
        /// </summary>
        /// <param name="data">The data.</param>
        public void Update(T data)
        {
            try
            {
                AffectedRows = new Collection<T>();
                OpenConnection(ConnectionObject);
                SqlTransaction transaction = ConnectionObject.BeginTransaction();
                Update(data as AbstractEntity, transaction);
                if (IsFake)
                {
                    AffectedRows.Add(data);
                    transaction.Rollback();
                }
                else
                {
                    transaction.Commit();
                }
            }
            finally
            {
                CloseConnection(ConnectionObject);
            }
        }

        /// <summary>
        /// Updates the specified data.
        /// </summary>
        /// <param name="data">The data.</param>
        /// <param name="isCascading">if set to <c>true</c> [is cascading].</param>
        public void Update(T data, bool isCascading)
        {
            if (isCascading)
            {
                AffectedRows = new Collection<T>();
                OpenConnection(ConnectionObject);
                SqlTransaction transaction = null;
                try
                {
                    transaction = ConnectionObject.BeginTransaction();
                    Update(data as AbstractEntity, transaction);
                    if (IsFake)
                    {
                        AffectedRows.Add(data);
                        transaction.Rollback();
                    }
                    else
                    {
                        transaction.Commit();
                    }
                }
                catch (InvalidOperationException exception)
                {
                    throw new MockException(exception.Message, "DataAccess");
                }
                catch (SqlException exception)
                {
                    throw new MockException(exception.Message, "DataAccess");
                }
                catch (MockException)
                {
                    if (transaction != null)
                        transaction.Rollback();
                    throw;
                }
                finally
                {
                    CloseConnection(ConnectionObject);
                }
            }
            else
            {
                Update(data);
            }
        }

        /// <summary>
        /// Deletes the specified id.
        /// </summary>
        /// <param name="id">The id.</param>
        public void Delete(object id)
        {
            try
            {
                AffectedRows = new Collection<T>();
                OpenConnection(ConnectionObject);
                SqlTransaction transaction = ConnectionObject.BeginTransaction();
                Delete(id, typeof(T), transaction);
                if (IsFake)
                {
                    AffectedRows.Add(GetById(id));
                    transaction.Rollback();
                }
                else
                {
                    transaction.Commit();
                }
            }
            finally
            {
                CloseConnection(ConnectionObject);
            }
        }
        #endregion
    }
}
